//
// Created by Wei on 2022/5/16.
//
#include <vector>
#include <cstring>
#include "page.h"
#include "../enum/page_type.h"
#include "compact_only_id.h"
#include "../buffer/buffer_pool.h"
#include <climits>

#define PAGE_NOT_SUPPORT -2

void PAGE::init(PAGE *page) {
    page->file_header.setFilPageOffset(0);
    page->file_header.setFilePagePrev(UINT_MAX);
    page->file_header.setFilPageNext(UINT_MAX);
    page->file_header.setFilPageType(FIL_PAGE_TYPE_ALLOCATED);

    page->page_header.setPageHeapTop(data_offset);
    page->page_header.setPageNHeap(2);//最大最小
    page->page_header.setPageGarbage(0);
    struct INFIMUM_PART infi;
    struct SUPER_PART supermum;

    infi.header.set_record_type(2);
    infi.header.set_n_owned(1);
    infi.header.set_heap_no(0);
    infi.header.set_next_record(supermum_offset);
    supermum.header.set_record_type(3);
    supermum.header.set_n_owned(1);
    supermum.header.set_heap_no(1);
    supermum.header.set_next_record(infimum_offset);

    memcpy(&page->page_data[0], &infi, 13);
    memcpy(&page->page_data[13], &supermum, 13);
}

PAGE::PAGE(unsigned int page_offset, unsigned int prev, unsigned int next, unsigned int page_type) {
    init(this);
    file_header.setFilPageOffset(page_offset);
    file_header.setFilePagePrev(prev);
    file_header.setFilPageNext(next);
    file_header.setFilPageType(page_type);
}

PAGE::PAGE(unsigned int page_offset) {
    init(this);
    file_header.setFilPageOffset(page_offset);
}


PAGE::PAGE() {
    init(this);
}

unsigned short charArrToOffset(const void *start) {
    auto *s = (unsigned char *) start;
    unsigned short id = 0;
    for (unsigned char i = 0; i < 2; i++) {
        id <<= 8;
        id |= s[i];
    }
    return id;
}


COMPACT_HEADER *PAGE::findInsertPrev(const COMPACT_ONLY_ID *compact, unsigned short *preIndex) {
    //fix
    auto *pre = (COMPACT_HEADER *) &page_data[0];
    *preIndex = 0;
    unsigned short nextOffset = pre->get_next_record();
    while (nextOffset != supermum_offset) {
        auto *nextTemp = (COMPACT_HEADER *) &page_data[nextOffset];
        unsigned int tempId = ((COMPACT_ONLY_ID *) &page_data[nextOffset])->id;
        if (tempId < compact->id) {
            *preIndex = nextOffset;
            pre = nextTemp;
            nextOffset = pre->get_next_record();
        } else if (tempId == compact->id) {
            return nullptr;
        } else {
            break;
        }
    }
    return pre;
}

int PAGE::insertAtEnd(const COMPACT_ONLY_ID *compact) {
    unsigned short preIndex = 0;
    COMPACT_HEADER *pre = findInsertPrev(compact, &preIndex);
    if (pre == nullptr) {
        return -1;
    }
    //此时p为插入位置的前驱节点偏移位置
    unsigned short heapTop = page_header.getPageHeapTop();
    size_t size;
    switch (file_header.getFilPageType()) {
        case FIL_PAGE_DIR:
            size = sizeof(COMPACT_DIR);
            break;
        case FIL_PAGE_INDEX:
            size = sizeof(COMPACT_DATA);
            break;
        default:
            return PAGE_NOT_SUPPORT;
    }
    memcpy(&page_data[heapTop], compact, size);
    auto *insertHead = (COMPACT_HEADER *) &page_data[heapTop];
    insertHead->set_next_record(pre->get_next_record());
    pre->set_next_record(heapTop);
    page_header.setPageHeapTop(heapTop + size);
    page_header.setPageNHeap(page_header.getPageNHeap() + 1);
    page_header.setPageNRecs(page_header.getPageNRecs() + 1);
    return 0;
}


int PAGE::insertAtPieces(const COMPACT_ONLY_ID *compact) {
    unsigned short preIndex = 0;
    COMPACT_HEADER *pre = findInsertPrev(compact, &preIndex);
    if (pre == nullptr) {
        return -1;
    }
    //逻辑连接
    //garbage大于0说明对应位置地址在data里
    unsigned short tobeReusedOffset = page_header.getPageFree();
    auto beReused = (COMPACT_ONLY_ID *) &page_data[tobeReusedOffset];
    page_header.setPageFree(beReused->compact_header.get_next_record());//垃圾节点往后移
    //物理连接
    size_t size;
    switch (file_header.getFilPageType()) {
        case FIL_PAGE_DIR:
            size = sizeof(COMPACT_DIR);
            break;
        case FIL_PAGE_INDEX:
            size = sizeof(COMPACT_DATA);
            break;
        default:
            return PAGE_NOT_SUPPORT;
    }
    memcpy(beReused, compact, size);

    beReused->compact_header.set_next_record(pre->get_next_record());
    pre->set_next_record(tobeReusedOffset);
    page_header.setPageGarbage(page_header.getPageGarbage() - size);
    page_header.setPageNRecs(page_header.getPageNRecs() + 1);
    return 0;
}

//只做分裂，返回0说明插入位置应该再本页，返回1说明插入位置在新页，返回-1说明不成功
//只分裂不插入
//warn 空页不能用这个方法
int PAGE::split(unsigned int id) {
    //右插入

    PAGE *nextPage = BufferPool::getInstance()->allocate();

    nextPage->file_header.setFilPageType(file_header.getFilPageType());
    nextPage->file_header.setFilePagePrev(file_header.getFilPageOffset());
    nextPage->file_header.setFilPageNext(file_header.getFilPageNext());
    nextPage->file_header.setFilPageParent(file_header.getFilPageParent());
    nextPage->page_header.setPageLevel(page_header.getPageLevel());
    unsigned int nextPageOffset = nextPage->file_header.getFilPageOffset();
    file_header.setFilPageNext(nextPageOffset);
    unsigned short mid_pre_offset = infimum_offset;
    unsigned short need_move_offset = ((COMPACT_HEADER *) &page_data[infimum_offset])->get_next_record();
    //需要移动到下一页的起始位置
    //如果用槽，直接就从槽拿一半了..
    int res = 0;
    //肯定至少有两条记录才会走到分裂吧..
    unsigned int firstId = ((COMPACT_ONLY_ID *) &page_data[need_move_offset])->id;
    //fix
    if (id < firstId) {
        firstId = id;
    }
    for (int i = 0; i < page_header.getPageNRecs() / 2; i++) {
        mid_pre_offset = need_move_offset;
        need_move_offset = ((COMPACT_HEADER *) &page_data[need_move_offset])->get_next_record();
    }
    ((COMPACT_HEADER *) &page_data[mid_pre_offset])->set_next_record(supermum_offset);
    COMPACT_DIR nextPageIndex{}, curPageIndex{};//下一页与这一页的目录项
    curPageIndex.id = firstId;
    curPageIndex.page = file_header.getFilPageOffset();
    unsigned short record_type;
    switch (file_header.getFilPageType()) {
        case FIL_PAGE_DIR:
            record_type = 1;
            break;
        case FIL_PAGE_INDEX:
            record_type = 0;
            break;
        default:
            return - PAGE_NOT_SUPPORT;
    }
    nextPageIndex.compact_header.set(false, false, 0, 0, record_type, 0);
    unsigned short removeCnt = 0;
    page_header.setPageFree(need_move_offset);
    while (need_move_offset != supermum_offset) {
        unsigned int checkId = ((COMPACT_ONLY_ID *) &page_data[need_move_offset])->id;
        if (nextPageIndex.id == 0) {
            //最小的作为上级目录
            nextPageIndex.id = checkId;
            if (checkId <= id) {
                res = 1;
            }
        }
        if (file_header.getFilPageType() == FIL_PAGE_DIR) {
            nextPage->insert((COMPACT_DIR *) &page_data[need_move_offset]);
        } else {
            nextPage->insert((COMPACT_DATA *) &page_data[need_move_offset]);
        }
        removeCnt++;
        need_move_offset = ((COMPACT_HEADER *) &page_data[need_move_offset])->get_next_record();
    }
    nextPageIndex.page = nextPageOffset;
    page_header.setPageNRecs(page_header.getPageNRecs() - removeCnt);
    //根节点比较特殊
    if (page_header.getPageLevel() == 0) {
        PAGE *newRoot = BufferPool::getInstance()->allocate();
        newRoot->file_header.setFilPageType(FIL_PAGE_DIR);
        newRoot->page_header.setPageLevel(0);

        page_header.setPageLevel(1);
        nextPage->page_header.setPageLevel(1);
        file_header.setFilPageParent(newRoot->file_header.getFilPageOffset());
        nextPage->file_header.setFilPageParent(newRoot->file_header.getFilPageOffset());

        BufferPool::getInstance()->set_root(newRoot);
        newRoot->insert(&curPageIndex);
    }
    PAGE *cur_parent = BufferPool::getInstance()->allocate(file_header.getFilPageParent());
    cur_parent->insert(&nextPageIndex);//上层已经更新，插入会-1也没问题

    if (file_header.getFilPageType() == FIL_PAGE_DIR) {
        page_header.setPageGarbage(page_header.getPageGarbage() + removeCnt * sizeof(COMPACT_DIR));
    } else if (file_header.getFilPageType() == FIL_PAGE_INDEX) {
        page_header.setPageGarbage(page_header.getPageGarbage() + removeCnt * sizeof(COMPACT_DATA));
    } else {
        return PAGE_NOT_SUPPORT;
    }

    return res;
}

void PAGE::updateMinId(unsigned int id) {
    auto *check = (COMPACT_HEADER *) &page_data[infimum_offset];
    check = (COMPACT_HEADER *) &page_data[check->get_next_record()];
    auto *temp = (COMPACT_ONLY_ID *) check;
    temp->id = id;
}

unsigned int PAGE::getMinId() {
    if (page_header.getPageNRecs() == 0) {
        return 0;
    }
    auto *check = (COMPACT_HEADER *) &page_data[infimum_offset];
    check = (COMPACT_HEADER *) &page_data[check->get_next_record()];
    auto *temp = (COMPACT_ONLY_ID *) check;
    return temp->id;
}

int PAGE::insert(COMPACT_DATA *compact) {
//    BufferPool::getInstance()->lock(this);
    int res = insertWithoutLock(compact);
//    BufferPool::getInstance()->unlock(this);
    return res;
}

int PAGE::insert(COMPACT_DIR *compact) {
//    BufferPool::getInstance()->lock(this);
    int res = insertWithoutLock(compact);
//    BufferPool::getInstance()->unlock(this);
    return res;
}

COMPACT_DATA *PAGE::search(unsigned int id) {
    unsigned short prevOffset = 0;
    auto *start = (COMPACT_HEADER *) &page_data[prevOffset];
    unsigned short curOffset = start->get_next_record();
    while (curOffset != supermum_offset) {
        auto *temp = (COMPACT_ONLY_ID *) &page_data[curOffset];
        if (temp->id < id) {
            prevOffset = curOffset;
            curOffset = temp->compact_header.get_next_record();
            //好像一直root一直最多只有两页
        } else if (temp->id == id) {
            //此时是数据页，那么直接返回
            if (file_header.getFilPageType() == FIL_PAGE_INDEX) {
                return reinterpret_cast<COMPACT_DATA *>(temp);
            }
            prevOffset = curOffset;
            break;
        } else {
            //此时数据页，已经找不到了
            if (file_header.getFilPageType() == FIL_PAGE_INDEX) {
                return nullptr;
            }
            break;
        }
    }
    //prevOffset指向的页应该是小于等于的，此时为目录页
    if (prevOffset == infimum_offset) {
        return nullptr;
    }
    auto *dir = (COMPACT_DIR *) &page_data[prevOffset];
    PAGE *next = BufferPool::getInstance()->allocate(dir->page, true);
    return next->search(id);
}

void PAGE::setPageNo(size_t page_no) {
    file_header.setFilPageOffset(page_no);
}

size_t PAGE::getPageNo() {
    return file_header.getFilPageOffset();
}


PAGE *PAGE::searchNear(unsigned int id) {
    unsigned short prevOffset = 0;
    auto *start = (COMPACT_HEADER *) &page_data[prevOffset];
    unsigned short curOffset = start->get_next_record();
    while (curOffset != supermum_offset) {
        auto *temp = (COMPACT_ONLY_ID *) &page_data[curOffset];
        if (temp->id < id) {
            prevOffset = curOffset;
            curOffset = temp->compact_header.get_next_record();
            //好像一直root一直最多只有两页
        } else if (temp->id == id) {
            prevOffset = curOffset;
            break;
        } else {
            break;
        }
    }
    if (prevOffset == infimum_offset) {
        //说明起始位置比第一条记录要小，直接找root的第一个
        auto *root = BufferPool::getInstance()->get_root();
        auto *rootFirst = (COMPACT_ONLY_ID *) &root->page_data[infimum_offset];
        if (rootFirst->compact_header.get_next_record() == supermum_offset) {
            return nullptr;//root也没数据了
        }
        rootFirst = (COMPACT_ONLY_ID *) &root->page_data[rootFirst->compact_header.get_next_record()];
        return root->searchNear(rootFirst->id);
    }
    if (file_header.getFilPageType() == FIL_PAGE_DIR) {
        auto *dir = (COMPACT_DIR *) &page_data[prevOffset];
        PAGE *next = BufferPool::getInstance()->allocate(dir->page, true);

        if (next->file_header.getFilPageType() == FIL_PAGE_DIR) {
            return next->searchNear(id);
        } else {
            return next;
        }
    } else {
        return this;
    }


}

int PAGE::search(unsigned int start, unsigned int end, COMPACT_DATA **receptor) {
    PAGE *curPage = searchNear(start);
    if (curPage == nullptr) {
        return 0;
    }
    int index = 0;
    unsigned short pageInnerOffset = infimum_offset;
    auto *pageInnerP = (COMPACT_DATA *) &curPage->page_data[pageInnerOffset];
    pageInnerOffset = pageInnerP->compact_header.get_next_record();
    pageInnerP = (COMPACT_DATA *) &curPage->page_data[pageInnerOffset];
    while (true) {
        if (pageInnerOffset == supermum_offset) {
            unsigned int nextPageOffset = curPage->file_header.getFilPageNext();
            if (nextPageOffset == UINT_MAX) {
                break;
            }
            curPage = BufferPool::getInstance()->allocate(nextPageOffset);
            if (curPage == nullptr)
                break;
            pageInnerOffset = infimum_offset;
            pageInnerP = (COMPACT_DATA *) &curPage->page_data[pageInnerOffset];
            pageInnerOffset = pageInnerP->compact_header.get_next_record();
            pageInnerP = (COMPACT_DATA *) &curPage->page_data[pageInnerOffset];
        }
        unsigned int id = pageInnerP->id;
        if (id >= start && id <= end) {
            receptor[index] = new COMPACT_DATA();
            memcpy(receptor[index], pageInnerP, sizeof(COMPACT_DATA));
            index++;
        } else if (id > end) {
            break;
        }
        pageInnerOffset = pageInnerP->compact_header.get_next_record();
        pageInnerP = (COMPACT_DATA *) &curPage->page_data[pageInnerOffset];
    }
    return index;
}

std::vector<COMPACT_DATA> PAGE::search(unsigned int start, unsigned int end) {
    std::vector<COMPACT_DATA> res = std::vector<COMPACT_DATA>();
    PAGE *curPage = searchNear(start);
    if (curPage == nullptr) {
        return res;
    }
    unsigned short pageInnerOffset = infimum_offset;
    auto *pageInnerP = (COMPACT_DATA *) &curPage->page_data[pageInnerOffset];
    pageInnerOffset = pageInnerP->compact_header.get_next_record();
    pageInnerP = (COMPACT_DATA *) &curPage->page_data[pageInnerOffset];
    while (true) {
        if (pageInnerOffset == supermum_offset) {
            unsigned int nextPageOffset = curPage->file_header.getFilPageNext();
            if (nextPageOffset == UINT_MAX) {
                break;
            }
            curPage = BufferPool::getInstance()->allocate(nextPageOffset);
            if (curPage == nullptr)
                break;
            pageInnerOffset = infimum_offset;
            pageInnerP = (COMPACT_DATA *) &curPage->page_data[pageInnerOffset];
            pageInnerOffset = pageInnerP->compact_header.get_next_record();
            pageInnerP = (COMPACT_DATA *) &curPage->page_data[pageInnerOffset];
        }
        unsigned int id = pageInnerP->id;
        if (id >= start && id <= end) {
            COMPACT_DATA c{};
            memcpy(&c, pageInnerP, sizeof(COMPACT_DATA));
            res.push_back(c);
        } else if (id > end) {
            break;
        }
        pageInnerOffset = pageInnerP->compact_header.get_next_record();
        pageInnerP = (COMPACT_DATA *) &curPage->page_data[pageInnerOffset];
    }
    return res;
}

int PAGE::insertWithoutLock(COMPACT_DIR *compact) {
    if (this->file_header.getFilPageType() != FIL_PAGE_DIR) {
        return PAGE_NOT_SUPPORT;
    }
    unsigned int curMinId = getMinId();
    if (curMinId > compact->id) {
        //对于目录项，插入的id小于最小id，直接更新
        updateMinId(compact->id);
        if (page_header.getPageLevel() != 0) {
            BufferPool::getInstance()->allocate(file_header.getFilPageParent())->updateMinId(compact->id);
        }
        return 0;
    } else if (curMinId == compact->id) {
        return -1;
    }
    //非叶子节点才会调用此方法
    unsigned short heapTop = page_header.getPageHeapTop();
    unsigned short garbage_size = page_header.getPageGarbage();
    size_t compact_size = sizeof(COMPACT_DIR);
    if (FREE_DATA_SIZE + CONST_DATA_SIZE - heapTop >= compact_size) {
        PAGE *child = BufferPool::getInstance()->allocate(compact->page);
        child->file_header.setFilPageParent(file_header.getFilPageOffset());
        return insertAtEnd((COMPACT_ONLY_ID *) (compact));
    } else if (garbage_size >= compact_size) {
        PAGE *child = BufferPool::getInstance()->allocate(compact->page);
        child->file_header.setFilPageParent(file_header.getFilPageOffset());
        //尾部连续空间不足，但是垃圾空间里有
        return insertAtPieces((COMPACT_ONLY_ID *) (compact));
    } else {
        int direction = split(compact->id);
        PAGE *page;
        switch (direction) {
            case 0:
                return insert(compact);
            case 1:
                page = BufferPool::getInstance()->allocate(file_header.getFilPageNext());
                return page->insert(compact);
            default:
                return PAGE_NOT_SUPPORT;
        }
    }
}

int PAGE::insertWithoutLock(COMPACT_DATA *compact) {
    if (file_header.getFilPageType() == FIL_PAGE_DIR) {
        unsigned int curMinId = getMinId();
        if (curMinId > compact->id) {
            //对于目录项，插入的id小于最小id，直接更新
            updateMinId(compact->id);
        } else if (curMinId == compact->id) {
            return -1;
        }
        //对于存储目录的，要继续往下找
        //二分先不管了
        unsigned short prevOffset = infimum_offset;
        auto *start = (COMPACT_HEADER *) &page_data[prevOffset];
        unsigned short curOffset = start->get_next_record();
        while (curOffset != supermum_offset) {
            auto *temp = (COMPACT_DIR *) &page_data[curOffset];
            if (temp->id <= compact->id) {
                prevOffset = curOffset;
                curOffset = temp->compact_header.get_next_record();
            } else {
                break;
            }
        }
        auto *indexPageDir = (COMPACT_DIR *) &page_data[prevOffset];
//        PAGE *indexPage = BufferPool::getInstance()->allocate(indexPageDir->page);
        PAGE *indexPage = BufferPool::getInstance()->allocate(indexPageDir->page);
        return indexPage->insert(compact);
    } else if (file_header.getFilPageType() == FIL_PAGE_INDEX) {
        //这里说明已经到叶子节点了
        unsigned short heapTop = page_header.getPageHeapTop();
        unsigned short garbage_size = page_header.getPageGarbage();
        size_t compact_size = sizeof(COMPACT_DATA);
        if (FREE_DATA_SIZE + CONST_DATA_SIZE - heapTop >= compact_size) {
            //尾部连续空间足够
            return insertAtEnd((COMPACT_ONLY_ID *) (compact));
        } else if (garbage_size >= compact_size) {
            //尾部连续空间不足，但是垃圾空间里有
            return insertAtPieces((COMPACT_ONLY_ID *) (compact));
        } else {
            int direction = split(compact->id);
            PAGE *page;
            switch (direction) {
                case 0:
                    return insert(compact);
                case 1:
                    page = BufferPool::getInstance()->allocate(file_header.getFilPageNext());
                    return page->insert(compact);
                default:
                    return PAGE_NOT_SUPPORT;
            }
        }
    } else {
        return PAGE_NOT_SUPPORT;
    }
}